﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ditec.Zep.DSigXades.Plugins;
using Ditec.Zep.DSigXades;
using System.Xml;
using System.Windows.Forms;
using System.IO;
using Org.BouncyCastle.Tsp;
using Org.BouncyCastle.Asn1;

namespace Ekomunikacia
{
    public class Sign
    {        
        public Sign() { }

        public string SignXml(string objectId, string xmlDescription, string xmlPath, string xsdPath, string xmlNamespaceURI, string xsdReferenceUri, string xslPath, string xslReferenceUri) 
        {
            string error = string.Empty;
            Object obj = new Object();

            XadesSig oDsig = new XadesSig(); //vytvorenie objektu XadesSig
            XmlPlugin xmlPlug = new XmlPlugin(); //vytvorenie objektu XmlPluginu

            //ncaitanie xml
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(xmlPath);

            //nacitanie xsd
            XmlDocument xsdDoc = new XmlDocument();
            xsdDoc.Load(xsdPath);

            //nacitanie xslt
            XmlDocument xslDoc = new XmlDocument();
            xslDoc.Load(xslPath);

            //na vyskusanie - ked necham vo for cykle tam mi to spravi zalozky
            //podpise sa to samozrejme do jedneho dokumentu
            //for (int i = 0; i < 4; i++)
            //{
                obj = xmlPlug.CreateObject("menoObjektu", xmlDescription, xmlDoc.DocumentElement.OuterXml, xsdDoc.DocumentElement.OuterXml, xmlNamespaceURI, xsdReferenceUri, xslDoc.DocumentElement.OuterXml, xslReferenceUri);
                if (obj == null)
                    return xmlPlug.ErrorMessage;

                //pridanie objektu do kolekcie
                if (oDsig.AddObject(obj) != 0)
                    return oDsig.ErrorMessage;
            //}
                        
            //sha1 je nepovolena podpisova politika
            if (oDsig.Sign("signatureId", "sha256", "urn:oid:1.3.158.36061701.0.0.1.10.4.0.12") == 0)
            {
                DialogResult res = MessageBox.Show("Chcete podpisane XML ulozit na disk?", "Uspesne podpisane", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);

                if (res == DialogResult.Yes)
                {
                    try
                    {
                        //nerobit do xml - bude problem s overenim, radsej ulozit len do textaku s koncovkou xml
                        //XmlDocument newXml = new XmlDocument();
                        //newXml.LoadXml(oDsig.SignedXmlWithEnvelope);
                        StreamWriter sw;

                        //spustenie file save dialogu
                        SaveFileDialog saveFileDialog1 = new SaveFileDialog();
                        saveFileDialog1.Filter = "Xml file|*.xml";
                        saveFileDialog1.Title = "Uložiť podpísané XML";
                        saveFileDialog1.RestoreDirectory = true;

                        if (saveFileDialog1.ShowDialog() == DialogResult.OK)
                        {
                            sw = new StreamWriter(saveFileDialog1.FileName);
                            sw.Write(oDsig.SignedXmlWithEnvelope);
                            sw.Close();
                            //f.
                            //newXml.Save(saveFileDialog1.FileName);
                        }
                    }
                    catch (Exception e)
                    {
                        return e.ToString();
                    }
                }
            }
            else
                return oDsig.ErrorMessage;
            
            return error;
        }

        public string AddTimeStamp()
        {
            string signatureData = string.Empty;
            string timestamp = string.Empty;
            
            OpenFileDialog oFile = new OpenFileDialog();
            oFile.Filter = "Xml file|*.xml";
            oFile.Title = "Vyberte xml súbor na pridanie časovej pečiatky";

            if (oFile.ShowDialog() == DialogResult.OK)
            {   
                try
                {
                    //ziskanie ds:signatureValue
                    signatureData = GetSignatureValue(oFile.FileName);

                    if (String.IsNullOrEmpty(signatureData))
                    {
                        throw new Exception("Ziskane signature value z podpisaneho dokumentu je prazdny retazec, co asi nie je ok");
                    }

                    //ziskanie casovej peciatky
                    timestamp = GetTimeStamp(signatureData);

                    if (String.IsNullOrEmpty(timestamp))
                    {
                        throw new Exception("Aplikacii sa nepodarilo ziskat udaje pre casovu peciatku z webovej sluzby, program bude ukonceny!");
                    }
                    else
                    {
                        DialogResult res = MessageBox.Show("Casovy udaj sa podarilo ziskat. \r\nChcete ulozit vysledny opeciatkovany dokument?", "Casovy udaj ziskany", MessageBoxButtons.YesNo, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);

                        if (res == DialogResult.Yes)
                        {
                            ///teraz to treba pridat do podpisaneho xml
                            AddToSignedXml(oFile.FileName, timestamp);
                        }                    
                    }
                }
                catch (Exception ex)
                {
                    DialogResult res = MessageBox.Show(ex.ToString(), "Chyba - program bude ukonceny", MessageBoxButtons.OK, MessageBoxIcon.Stop, MessageBoxDefaultButton.Button1);
                    if (res == DialogResult.OK)
                    {
                        //stop program
                        Form1.ActiveForm.Close();
                        Form1.ActiveForm.Dispose();
                    }
                }

            }
            return null;
        }

        /// <summary>
        /// metoda sluzi na ziskanie base64 stringu casovej peciatky
        /// 
        /// http://test.ditec.sk/timestampws/TS.asmx - link na ziskanie casoveho udaju
        /// 
        /// The returned time-stamp is a signed data object that contains the hash value, the identity of the TSA, 
        /// and the time of stamping.        /// 
        /// The SignatureTimeStamp encapsulates the time-stamp over the ds:SignatureValue element.
        /// 
        /// tazke musim vybrat informacie o podpise -> ds:signatureValue
        /// </summary>
        /// <param name="signatureValue">signature hodnota z podpisaneho xml suboru</param>
        /// <returns></returns>
        private string GetTimeStamp(string signatureValue)
        {
            string response = string.Empty;
            
            //vytvorenie SOAP klienta
            wsTimeStamp.TSSoapClient ws = new wsTimeStamp.TSSoapClient();

            try
            {
                //ziskam data z webovej sluzby v base64 stringu
                response = ws.GetTimestamp(signatureValue);

                ///spracovanie odpovede z webovej sluzby
                byte [] bResponse = new byte[response.Length];
                bResponse = Convert.FromBase64String(response);
                TimeStampResponse tsResponse = new TimeStampResponse(bResponse);
                //Asn1InputStream inputStream = new Asn1InputStream(tsResponse.TimeStampToken.GetEncoded());
                response = Convert.ToBase64String(tsResponse.TimeStampToken.GetEncoded());

            }
            catch (Exception ex)
            {
                throw ex;
            }           

            return response;
        }

        /// <summary>
        /// ziska basa64 string - zahashovanu cast certifikatu (podpisu)
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private string GetSignatureValue(string fileName)
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(fileName);
            string result = string.Empty;

            try
            {
                result = xmlDoc.GetElementsByTagName("ds:SignatureValue")[0].InnerText; //tento retazec bude v base64 formate
            }
            catch
            {
                throw new Exception("Nepodarilo sa ziskat SignatureValue z podpisaneho dokumentu");
            }

            return result;
        }

        /// <summary>
        /// pridava sa to tak, ze si vytvorime nove xmlNody do xades:
        /// - UnsignedProperties
        /// - SignatureTimeStamp
        /// The SignatureTimeStamp element is an unsigned property qualifying the signature.
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="timestamp"></param>
        private void AddToSignedXml(string fileName, string timestamp)
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.PreserveWhitespace = true;
            try
            {
                xmlDoc.Load(fileName);
            }
            catch
            {
                throw new Exception("Nepodarilo sa otvorit podpisane XML");
            }

            XmlNamespaceManager ns = new XmlNamespaceManager(xmlDoc.NameTable);
            ns.AddNamespace("xades", "http://uri.etsi.org/01903/v1.3.2#");

            //vytvorenie elementov
            XmlElement unsignedProperties = xmlDoc.CreateElement("xades", "UnsignedProperties", "http://uri.etsi.org/01903/v1.3.2#");
            XmlElement signatureTimeStamp = xmlDoc.CreateElement("xades", "SignatureTimeStamp", "http://uri.etsi.org/01903/v1.3.2#");
            XmlElement unsignedSignatureProperties = xmlDoc.CreateElement("xades", "UnsignedSignatureProperties", "http://uri.etsi.org/01903/v1.3.2#");
            //EncapsulatedTimeStamp  typu EncapsulatedPKIDataType
            XmlElement encapsulatedTimeStamp = xmlDoc.CreateElement("xades", "EncapsulatedTimeStamp", "http://uri.etsi.org/01903/v1.3.2#");
            XmlAttribute id = xmlDoc.CreateAttribute("Id");
            id.Value = "1"; //tym sa moze referenovat archivna peciatka
            signatureTimeStamp.Attributes.Append(id);
                        
            //id.InnerText = "1";
            encapsulatedTimeStamp.InnerText = timestamp;
            
            signatureTimeStamp.AppendChild(encapsulatedTimeStamp);
            unsignedSignatureProperties.AppendChild(signatureTimeStamp);
            unsignedProperties.AppendChild(unsignedSignatureProperties);

            try
            {
                //ked uz mam pripravene data, co budem pridavat, pridam ich na spravne miesto v xml 
                //-> do QualifyingProperties za SignedProperties
                XmlNode q = xmlDoc.SelectSingleNode("//xades:QualifyingProperties", ns);
                q.InsertAfter(unsignedProperties, xmlDoc.SelectSingleNode("//xades:SignedProperties", ns));
            }
            catch
            {
                throw new Exception("Nepodarilo sa najst element xades:QualifyingProperties, alebo //xades:SignedProperties");
            }

            SaveFileDialog oFile = new SaveFileDialog();
            oFile.Filter = "Xml file|*.xml";
            oFile.Title = "Ulozenie opeciatkovaneho dokumentu";

            try
            {

                if (oFile.ShowDialog() == DialogResult.OK)
                {
                    StreamWriter sw = new StreamWriter(oFile.FileName);
                    sw.Write(xmlDoc.OuterXml);
                    sw.Close();
                }
            }
            catch
            {
                throw new Exception("Nepodarilo sa ulozit vysledny subor");
            }
        }
    }    
}
